import cloneDeep from 'lodash.clonedeep'
import { useState, useEffect } from 'react'
import CCheck, { CheckStatus, Formula } from '~/models/check'
import CNumber from '~/models/number'
import COperate, { IOPERATORS, OPERATORS } from '~/models/operate'
import { useFetcher } from 'remix'
import Drawer from './Drawer'
import Progress from './Progress'

function TaskItem ({ task, onAnswer }: { task: Formula, onAnswer: (data: Formula) => void }) {
    const [input, setInput] = useState<string | undefined>()

    const handleCheck = () => {
        if (!input) return

        const check = new CCheck(task, Number(input))
        onAnswer(check.result())
    }

    const handleReset = () => {
        onAnswer({ ...task, answer: input ? Number(input) : undefined })
    }

    return (
        <div className={`item ${task.status}`}>
            <div className='question'>
                <span>{task.first}</span>
                <span>{task.signLabel}</span>
                <span>{task.second}</span>
                <span>=</span>
            </div>
            <input
                type='text'
                className='answer'
                value={input}
                onFocus={handleReset}
                onBlur={handleCheck}
                onChange={e => setInput(e.target.value)}
            />
        </div>
    )
}

export default function Arithmetic () {
    const saveFetcher = useFetcher()
    const histories = useFetcher()
    const history = useFetcher()
    const [tasks, setTasks] = useState<Formula[]>([])
    const [historiesVisible, setHistoriesVisible] = useState(false)
    
    // params
    const [max, setMax] = useState(20)
    const [min, setMin] = useState(1)
    const [count, setCount] = useState(10)
    const [operators, setOperators] = useState([
        IOPERATORS.PLUS,
        IOPERATORS.SUBT
    ])

    const handleCheckOperator = (opt: IOPERATORS) => {
        if (operators.includes(opt)) {
            setOperators(opts => opts.filter(i => i !== opt))
        } else {
            setOperators([...operators, opt])
        }
    }

    const overRange = (num: number, _min?: number, _max?: number) => {
        return num < (_min || min) || num > (_max || max)
    }

    const handleRandom = async () => {
        let result: Formula[] = []
        for (var i = 0; i < count; i++) {
            const optIns = new COperate()
            const opt = optIns.randomIn(operators)

            const isSubt = opt.value === IOPERATORS.SUBT // 减法
            const isMult = opt.value === IOPERATORS.MULT // 乘法
            const isDivi = opt.value === IOPERATORS.DIVI // 除法

            const numberIns = new CNumber()
        
            let first = numberIns.rangeIn(min, max)
            let second = min
            if (isSubt) {
                second = numberIns.rangeIn(min, first)
            } else if (isMult) {
                let product = 1, multiplier = 1, times = 0
                do {
                    if (++times > 100) {
                        times = 0
                        first = numberIns.rangeIn(min, max)
                    }

                    multiplier = numberIns.rangeIn(min, max)
                    product = first * multiplier
                } while (overRange(multiplier, 1, max) || overRange(product, min, max * 10))

                second = multiplier
            } else if (isDivi) {
                let product = 1, multiplier = 1, times = 0
                do {
                    if (++times > 100) {
                        times = 0
                        first = numberIns.rangeIn(min, max)
                    }
                    product = numberIns.multiThen(first, max)
                    multiplier = Math.ceil(product / first)
                } while (overRange(multiplier, 1, max) || overRange(product, min, max * 10))

                second = multiplier
                first = product
            } else {
                let sum = 1, times = 0
                do {
                    if (++times > 100) {
                        times = 0
                        first = numberIns.rangeIn(min, max)
                    }
                    second = numberIns.rangeIn(min, max)
                    sum = first + second
                } while (overRange(sum))
            }

            result.push({ id: `${i}${first}${second}`, first, second, sign: opt.sign, signLabel: opt.signLabel, status: CheckStatus.UNDO })
        }

        setTasks(result)
    }

    const handleAnswer = (result: Formula, index: number) => {
        const ts = cloneDeep(tasks)
        ts[index] = result
        setTasks(ts)
    }

    const getPercent = (key: string, value: any, notEqual: boolean = false) => {
        const result = tasks.filter((i: any) => notEqual ? i[key] !== value : i[key] === value).length
        return Number((result / count).toFixed(2)) * 100
    }

    const renderProgress = () => {
        const progress = getPercent('status', CheckStatus.UNDO, true)

        return (
            <div className='inline'>
                <span className='mr10'>进度</span>
                <Progress percent={progress} />
            </div>
        )
    }

    const renderScore = () => {
        const score = getPercent('status', CheckStatus.CORRECT)

        return (
            <div className='inline'>
                <span className='mr10'>分数</span>
                <Progress percent={score} />
                <span className={`ml10 highlight ${score === 100 ? 'bigger' : score >= 80 ? 'green' : score >= 60 ? 'orange' : 'gray'}`}>{score}分</span>
            </div>
        )
    }

    const getHistories = () => {
        const form = new FormData()
        histories.submit(form, { action: '/api/getArithmetics', method: 'post' })
        setHistoriesVisible(true)
    }

    const getHistory = (filename: string) => {
        const form = new FormData()
        form.append('filename', filename)
        history.submit(form, { action: '/api/getArithmetic', method: 'post' })
        setHistoriesVisible(false)
    }

    const renderHistories = () => {
        const { data } = histories.data
        return (
            <Drawer
                visible={historiesVisible}
                label='Histories'
                onClose={() => setHistoriesVisible(false)}
            >
                {data.map((item: string) =>
                    <div className='history-item' onClick={() => getHistory(item)}>{item}</div>
                )}
            </Drawer>
        )
    }

    const handleSave = () => {
        if (tasks.length) {
            const form = new FormData()
            form.append('data', JSON.stringify(tasks))
            
            saveFetcher.submit(form, { action: '/api/storeArithmetic', method: 'post' })
        }
    }

    useEffect(() => {
        if (history.data && history.type === 'done') {
            setTasks(history.data.data)
        }
    }, [history.data, history.type])

    return (
        <div className='wrapper'>
            <div className='inline wrap mr20'>
                <label className='inline mr20 mb10'>
                    题目数：
                    <input value={count} onChange={e => setCount(Number(e.target.value))} type='text' />
                </label>
                <label className='inline mr20 mb10'>
                    最大数：
                    <input value={max} onChange={e => setMax(Number(e.target.value))} type='text' />
                </label>
                <label className='inline mr20 mb10'>
                    最小数：
                    <input value={min} onChange={e => setMin(Number(e.target.value))} type='text' />
                </label>
                <div className='operators'>
                    { OPERATORS.map(({label, value}) =>
                        <label key={value} className='operator'>
                            <input
                                checked={operators.includes(value)}
                                onChange={() => handleCheckOperator(value)}
                                type='checkbox'
                            />
                            {label}
                        </label>
                    )}
                </div>
                <button className='random mb10' onClick={handleRandom}>random</button>
            </div>
            {renderProgress()}
            <div className='list'>
                {tasks.map((item, index) =>
                    <TaskItem key={index + item.id} task={item} onAnswer={result => handleAnswer(result, index)} />
                )}
            </div>
            {renderScore()}
            <div className='inline'>
                <button className='flex1 mr10' onClick={getHistories}>List</button>
                <button className={`flex1 ${tasks.length ? '' : 'disabled'}`} onClick={handleSave}>save</button>
            </div>

            { histories.data && histories.type === 'done' ? renderHistories() : null }
        </div>
    )
}